package com.binance.assetservice.web.service.impl;

import com.binance.assetservice.data.models.PortfolioItem;
import com.binance.assetservice.vo.request.portfolio.AddPortfolioRequest;
import com.binance.assetservice.vo.request.portfolio.DeletePortfolioRequest;
import com.binance.assetservice.vo.request.portfolio.GetPortfoliosRequest;
import com.binance.assetservice.vo.request.portfolio.GetWidgetPortfoliosRequest;
import com.binance.assetservice.vo.request.portfolio.UpdatePortfolioSequenceRequest;
import com.binance.assetservice.vo.request.portfolio.UpdateWidgetPortfoliosRequest;
import com.binance.assetservice.web.cache.IPortfolioCache;
import com.binance.assetservice.web.cache.IWidgetPortfolioCache;
import com.binance.assetservice.web.service.IPortfolioService;
import com.binance.assetservice.web.service.IProductService;
import com.binance.master.error.BusinessException;
import com.binance.master.error.GeneralCode;
import com.binance.master.old.config.OldDB;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.javasimon.SimonManager;
import org.javasimon.Split;
import org.javasimon.aop.Monitored;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;
import java.util.Set;

@Monitored
@Service
@Slf4j
public class PortfolioServiceImpl extends BaseServiceImpl implements IPortfolioService {
    private final String PERFORMANCE_METRICS_NAME = this.getClass().getName() + ".fetchProducts";
    @Autowired
    private IPortfolioCache portfolioCache;
    @Autowired
    private IWidgetPortfolioCache widgetPortfolioCache;
    @Autowired
    private IProductService productService;

    @Override
    @Transactional(value = OldDB.TRANSACTION, rollbackFor = Exception.class)
    public void addPortfolio(AddPortfolioRequest body) throws Exception {
        this.getCheckedUser(body.getUserId());
        String[] arr = body.getSymbol().split(",");
        List<String> symbols = java.util.Arrays.asList(arr);
        for (String symbol : symbols) {
            PortfolioItem portfolioItem = new PortfolioItem();
            portfolioItem.setUserId(body.getUserId());
            portfolioItem.setSymbol(symbol);
            if (productService.get(symbol) == null) {
                throw new BusinessException(GeneralCode.PRODUCT_NOT_EXIST);
            }
            if (!portfolioCache.isExistPortfolio(portfolioItem)) {
                Integer order = this.portfolioCache.getLastSequence(body.getUserId());
                if (order == null) {
                    portfolioItem.setSequence(1);
                } else {
                    portfolioItem.setSequence(order + 1);
                }
                portfolioCache.addPortfolio(portfolioItem);
            }
        }
    }

    @Override
    @Transactional(value = OldDB.TRANSACTION, rollbackFor = Exception.class)
    public void deletePortfolio(DeletePortfolioRequest body) throws Exception {
        this.getCheckedUser(body.getUserId());
        portfolioCache.deletePortfolio(body.getUserId(), body.getSymbols());
    }

    @Override
    @Transactional(value = OldDB.TRANSACTION, rollbackFor = Exception.class)
    public void updatePortfolioSequence(UpdatePortfolioSequenceRequest body) throws Exception {
        this.getCheckedUser(body.getUserId());
        List<String> symbols = body.getSymbols();
        if (CollectionUtils.isEmpty(symbols)) {
            this.portfolioCache.deletePortfolio(body.getUserId(), symbols);
        } else {
            List<PortfolioItem> portfolioItems = Lists.newArrayList();
            List<String> oldList = portfolioCache.getPortfolios(body.getUserId());
            if (oldList == null) {
                oldList = Lists.newArrayList();
            }
            Set<String> set1 = Sets.newHashSet(oldList.iterator());
            Set<String> set2 = Sets.newHashSet(symbols.iterator());
            List<String> deffList = Arrays.asList(Sets.symmetricDifference(set1, set2).toArray(new String[] {}));
            this.portfolioCache.deletePortfolio(body.getUserId(), deffList);
            for (int i = 0; i < symbols.size();) {
                PortfolioItem productItem = new PortfolioItem();
                productItem.setSymbol(symbols.get(i));
                productItem.setSequence(++i);
                productItem.setUserId(body.getUserId());
                portfolioItems.add(productItem);
            }
            portfolioCache.changePortfolioSequence(portfolioItems);
        }
    }

    @Override
    public List<String> getPortfolios(GetPortfoliosRequest body) throws Exception {
        Split split = SimonManager.getStopwatch(PERFORMANCE_METRICS_NAME).start();
        try {
            log.debug("Received portfilio fetch request");
            List<String> list = portfolioCache.getPortfolios(body.getUserId());
            if (list == null) {
                return Lists.newArrayList();
            }
            return list;
        } finally {
            split.stop();
        }
    }

    @Override
    public List<String> getWidgetPortfolios(GetWidgetPortfoliosRequest body) throws Exception {
        log.info(String.format("widget/portfolios userId:%s", body.getUserId()));
        return widgetPortfolioCache.getWidgetPortfoliosByUserId(body.getUserId());
    }

    @Override
    @Transactional(value = OldDB.TRANSACTION, rollbackFor = Exception.class)
    public void updateWidgetPortfolios(UpdateWidgetPortfoliosRequest body) throws Exception {
        log.info(
                String.format("widget/updateWidgetPortfolio userId:%s,symbol:%s", body.getUserId(), body.getSymbols()));
        this.getCheckedUser(body.getUserId());
        if (CollectionUtils.isNotEmpty(body.getSymbols())) {
            for (String symbol : body.getSymbols()) {
                if (productService.get(symbol) == null) {
                    throw new BusinessException(GeneralCode.PRODUCT_NOT_EXIST);
                }
            }
        }
        this.widgetPortfolioCache.updateWidgetPortfolio(body.getUserId(), body.getSymbols());

    }

}
